package com.sbm.datasource.dynamic;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import com.sbm.datasource.config.MyBatisConfig;
import com.sbm.datasource.config.DataSourceConfig;

/**
 * 多数据源注册
 *
 * @author Administrator
 */
@Configuration
@EnableTransactionManagement
public class DynamicDataSourceConfigurer implements TransactionManagementConfigurer {
	private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSourceConfigurer.class);

	@Autowired
	private DynamicDataSourceConfig config;

	@Autowired
	private MyBatisConfig mybatis;

	@Bean(name = "DynamicDataSource")
	public DataSource dataSource() throws Exception {
		this.config.initialize();
		DynamicDataSourceRouter dynamicDataSource = new DynamicDataSourceRouter();
		dynamicDataSource.setDefaultTargetDataSource(defaultDataSource());
		Map<Object, Object> dsMap = new HashMap<Object, Object>();
		// 默认数据源
		dsMap.put("default", defaultDataSource());
		// 其它数据源
		dsMap.putAll(customDataSource());
		dynamicDataSource.setTargetDataSources(dsMap);
		return dynamicDataSource;
	}

	@Bean(name = "DynamicSqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory() throws Exception {
		LOGGER.info("————创建SqlSessionFactory————");
		try {
			String typeAliasesPackage = this.mybatis.getTypeAliasesPackage();
			String typeHandlersPackage = this.mybatis.getTypeHandlersPackage();
			LOGGER.info("对象扫描路径：" + typeAliasesPackage);
			LOGGER.info("类型扫描路径：" + typeHandlersPackage);
			SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
			factoryBean.setDataSource(dataSource());
			factoryBean.setTypeAliasesPackage(typeAliasesPackage);
			factoryBean.setTypeHandlersPackage(typeHandlersPackage);
			SqlSessionFactory factory = factoryBean.getObject();
			LOGGER.info("SqlSessionFactory创建成功：" + factory.getClass().getName());
			return factory;
		} catch (Exception ex) {
			LOGGER.info("创建SqlSessionFactory失败：" + ex.getMessage());
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}
	}

	@Bean
	public SqlSessionTemplate sqlSessionTemplate() throws Exception {
		LOGGER.info("————创建SqlSessionTemplate————");
		try {
			SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory());
			LOGGER.info("SqlSessionTemplate创建成功：" + template.getClass().getName());
			return template;
		} catch (Exception ex) {
			LOGGER.info("创建SqlSessionTemplate失败：" + ex.getMessage());
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}
	}

	@Bean
	public DataSource defaultDataSource() throws Exception {
		LOGGER.info("————默认数据源【初始化】————");
		DataSourceConfig dataSourceConfig = this.config.getDefaultDataSourceConfig().getDataSourceConfig();
		DataSource dataSource = initDataSource(dataSourceConfig);
		return dataSource;
	}

	@Bean
	public Map<String, DataSource> customDataSource() throws Exception {
		LOGGER.info("————其它数据源【初始化】————");
		Map<String, DataSource> dataSources = new HashMap<String, DataSource>();
		List<DataSourceConfig> list = this.config.getCustomDataSourceConfig().getDataSourceConfigList();
		for (DataSourceConfig dataSourceConfig : list) {
			if (dataSources.containsKey(dataSourceConfig.getName()))
				continue;
			DataSource dataSource = initDataSource(dataSourceConfig);
			dataSources.put(dataSourceConfig.getName(), dataSource);
			DynamicDataSourceHolder.addDataSource(dataSourceConfig.getName());
		}
		return dataSources;
	}

	private DataSource initDataSource(DataSourceConfig dataSourceConfig) throws Exception {
		DataSource dataSource = this.buildDataSource(dataSourceConfig);
		bindData(dataSource, dataSourceConfig);
		return dataSource;
	}

	@SuppressWarnings("unchecked")
	private DataSource buildDataSource(DataSourceConfig dataSourceConfig) throws Exception {
		LOGGER.info("数据源：" + dataSourceConfig.getName());
		LOGGER.debug("类型：" + dataSourceConfig.getType());
		LOGGER.debug("驱动：" + dataSourceConfig.getDriverClassName());
		LOGGER.debug("连接串" + dataSourceConfig.getUrl());
		LOGGER.debug("用户" + dataSourceConfig.getName());
		LOGGER.debug("密码" + dataSourceConfig.getPassword());
		LOGGER.debug("————————————");
		DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
		dataSourceBuilder.type((Class<? extends DataSource>) Class.forName(dataSourceConfig.getType()));
		dataSourceBuilder.driverClassName(dataSourceConfig.getDriverClassName());
		dataSourceBuilder.url(dataSourceConfig.getUrl());
		dataSourceBuilder.username(dataSourceConfig.getUsername());
		dataSourceBuilder.password(dataSourceConfig.getPassword());
		return dataSourceBuilder.build();
	}

	private void bindData(DataSource dataSource, DataSourceConfig dataSourceConfig) {
		RelaxedDataBinder relaxedDataBinder = new RelaxedDataBinder(dataSource);
		relaxedDataBinder.setIgnoreNestedProperties(false);
		relaxedDataBinder.setIgnoreInvalidFields(false);
		relaxedDataBinder.setIgnoreUnknownFields(true);
		relaxedDataBinder.setConversionService(new DefaultConversionService());
		relaxedDataBinder.bind(new MutablePropertyValues(dataSourceConfig.getExtConfig()));
	}

	@Override
	public PlatformTransactionManager annotationDrivenTransactionManager() {
		try {
			PlatformTransactionManager mgr = new DataSourceTransactionManager(dataSource());
			LOGGER.info("————事务管理器【初始化】————");
			LOGGER.info("管理器初始化成功：" + mgr.getClass().getName());
			return mgr;
		} catch (Exception ex) {
			LOGGER.info("————事务管理器【初始化】————");
			LOGGER.info("管理器初始化失败：" + ex.getMessage());
			ex.printStackTrace();
			throw new RuntimeException(ex);
		}
	}
}
